///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file formic/wfn/wfn.h
///
/// \brief   header file for the wavefunction class
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef FORMIC_WFN_HEADER
#define FORMIC_WFN_HEADER

#include<map>
#include<string>

#include<boost/shared_ptr.hpp>

#include<formic/wfn/data.h>
#include<formic/wfn/coeff_obj.h>
#include<formic/wfn/command.h>

namespace formic {

  class Archive;
  class InputBase;

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  A class to represent a wavefunction.
  ///
  /// The wavefunction class represents a collection of wavefunction data, which are stored in
  /// WfnData objects and accessed by their names in _wi_map and _wf_map,
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  template<class S> class Wavefunction {

    public:

      /// \brief short name for the coefficient object map type
      typedef std::map<std::string, boost::shared_ptr<formic::CoeffObj<S> > > CMap;

      /// \brief short name for the integer data object map type
      typedef std::map<std::string, boost::shared_ptr<formic::WfnData<int> > > IMap;

      /// \brief short name for the floating point data object map type
      typedef std::map<std::string, boost::shared_ptr<formic::WfnData<S> > > FMap;

    private:

      /// \brief a counter tracking where to put the next fixed integer data in the overall integer data ordering
      int _fixed_int_pos;

      /// \brief a counter tracking where to put the next variable integer data in the overall integer data ordering
      int _var_int_pos;

      /// \brief a counter tracking where to put the next fixed floating point data in the overall integer data ordering
      int _fixed_float_pos;

      /// \brief a counter tracking where to put the next variable floating point data in the overall integer data ordering
      int _var_float_pos;

//      /// \brief  the object used to compute the overall wavefunction coefficient
//      boost::shared_ptr<pcps::CoeffObj<S> > _overall_coeff;

      /// \brief  A container organizing the coefficient objects by name.
      CMap _co_map;

      /// \brief  a container organizing the integer wavefunction data objects by name
      IMap _wi_map;

      /// \brief  a container organizing the floating point wavefunction data objects by name
      FMap _wf_map;

    private:

      void check_new_name(const std::string & name) const;

      template<class T> void add_wfn_data(const boost::shared_ptr<formic::WfnData<T> > & d,
                                          int & next_fix_pos,
                                          int & next_var_pos,
                                          std::map<std::string, boost::shared_ptr<formic::WfnData<T> > > & m);

      template<class T> void add_wfn_data(formic::Archive & arch,
                                          int & next_fix_pos,
                                          int & next_var_pos,
                                          std::map<std::string, boost::shared_ptr<formic::WfnData<T> > > & m);

    public:

      bool has_wi(const std::string & name) const { return (_wi_map.count(name) > 0); }
      bool has_wf(const std::string & name) const { return (_wf_map.count(name) > 0); }
      bool has_co(const std::string & name) const { return (_co_map.count(name) > 0); }

      boost::shared_ptr<formic::CoeffObj<S>  > get_co(const std::string & name);
      boost::shared_ptr<formic::WfnData<S>   > get_wf(const std::string & name);
      boost::shared_ptr<formic::WfnData<int> > get_wi(const std::string & name);

//      template<class T> void test_print(formic::Archive & arch, const std::map<std::string, boost::shared_ptr<T> > & m) {
//        typename std::map<std::string, boost::shared_ptr<T> >::const_iterator it;
//        for (it = m.begin(); it != m.end(); it++)
//          arch << *it->second;
//      }

      ~Wavefunction() {
        //if ( typeid(S) == typeid(double) ) {
        //  pcps::TextFileArchive a("temp.txt", "wt");
        //  this->test_print(a, _wi_map);
        //  this->test_print(a, _wf_map);
        //  this->test_print(a, _co_map);
        //  assert( _overall_coeff.get() != 0 );
        //  a << pcps::OverallCoeffCommand::keyword() << std::endl;
        //  a << _overall_coeff->name() << std::endl;
        //  a << std::endl;
        //  //pcps::BinaryFileArchive b("temp.bin", "wt");
        //  //this->test_print(b, _wi_map);
        //  //this->test_print(b, _wf_map);
        //}
      }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief constructs an empty wavefunction
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      Wavefunction() : _fixed_int_pos(-1), _var_int_pos(0), _fixed_float_pos(-1), _var_float_pos(0) {}

      void add_wfn_int_data(formic::Archive & arch);
      void add_wfn_float_data(formic::Archive & arch);
      void add_wfn_data(const boost::shared_ptr<formic::WfnData<int> > & d);
      void add_wfn_data(const boost::shared_ptr<formic::WfnData<S> > & d);

      void add_coeff_obj(const boost::shared_ptr<formic::CoeffObj<S> > & c);

      void dump_data_to_bin_arch(const std::string & filename);

      void clear_exact_sample_vecs() {
        for (typename CMap::iterator it = _co_map.begin(); it != _co_map.end(); it++)
          it->second->clear_exact_sample_vecs();
      }

//      ///////////////////////////////////////////////////////////////////////////////////////////////////
//      /// \brief sets the overall coefficient for the wavefunction
//      ///
//      /// \param[in]      s        the name of the coefficient object to use
//      ///
//      ///////////////////////////////////////////////////////////////////////////////////////////////////
//      void set_overall_coefficient(const std::string & s) {
//        if ( _co_map.count(s) == 0 )
//          throw pcps::Exception("no coefficient object with the name \"%s\" in Wavefunction::set_overall_coefficient.") % s;
//        _overall_coeff = _co_map[s];
//      }
//
//      /// \brief  returns a pointer to the overall wavefunction coefficient
//      boost::shared_ptr<pcps::CoeffObj<S> > & overall_coefficient() { return _overall_coeff; }

  };

}

#endif
